import { defineComponent, inject, isReactive, nextTick, PropType, provide, Ref, toRef } from "vue";
import { InnerPopupProps } from "../inner/InnerPopup";
import { deepCopy } from "../use/deepClone";

export type FormContextOptions = {
    labelWidth?: Ref<number>,
    inline?: boolean,
    model: any,
    onChange: (name: string, v: any) => void,
    setCheck: (name, checkFn) => void,
    setClearValid: (name, fn) => void,
    validation: any
    errorTransfer?: boolean
    errorAlign?: InnerPopupProps['align']
    rules?: Ref<{[key: string]: any}>,
}

export interface FormProps {
    labelWidth?: number,
    model?: any,
    inline?: boolean,
    onBeforeSubmit?: () => boolean | Promise<boolean>,
    autocomplete?: string
    validation?: any
    errorTransfer?: boolean
    errorAlign?: InnerPopupProps['align']
    rules?: {[key: string]: any},
}

export const FormContextKey = Symbol('FormContextKey');

export default defineComponent({
    name: 'Form',
    props: {
        labelWidth: { type: Number},
        inline: { type: Boolean},
        model: { type: Object},
        autocomplete: { type: String },
        validation: { type: Object },
        errorTransfer: { type: Boolean, default: false },
        errorAlign: { type: String as PropType<FormProps['errorAlign']>, default: 'right'},
        onBeforeSubmit: { type: Function as PropType<FormProps['onBeforeSubmit']> },
        rules: { type: Object as PropType<FormProps['rules']>}
    },
    emits: ['change', 'beforeSubmit'],
    setup (props: FormProps, { emit, slots, expose }) {
        const formCtx = inject(FormContextKey, null);
        if (formCtx) {
            console.warn('Form can not be nested');
        }

        const initModel = deepCopy(props.model);

        const _onChange = (name: string, v: any) => {
            // props.model[name] = v;
            emit('change', name, v);
            // 触发验证
            const check = checks.get(name);
            check && check(v);
        };

        const checks = new Map();
        const clears = new Map();

        const setCheck = (name, checkFn) => {
            checks.set(name, checkFn);
        };

        const setClearValid = (name, checkFn) => {
            clears.set(name, checkFn);
        };

        const ctx: FormContextOptions = {
            labelWidth: toRef(props.labelWidth),
            inline: props.inline,
            model: props.model,
            validation: props.validation,
            errorTransfer: props.errorTransfer,
            errorAlign: props.errorAlign,
            onChange: _onChange,
            setCheck,
            setClearValid,
            rules: toRef(props.rules),
        };

        provide(FormContextKey, ctx);

        /**
         * 获取值
         * @param obj
         * @param fields name值或数组
         * @returns
         */
        const _getValueByPath = (obj: any, fields: string) => {
            if (fields.includes(".")) {
                const fieldsArr = fields.split(".");
                return fieldsArr.reduce((pre, cur) => {
                    return pre[cur];
                }, obj);
            }
            return obj[fields];
        };


        /**
         * 根据路径获取值
         * @param fields name值或数组
         * @returns
         */
        const getValueByPath = (fields: string) => {
            return _getValueByPath(props.model, fields);
        };

        const onSubmit = (e: Event) => {
            e.preventDefault();
            if (props.onBeforeSubmit) {
                return props.onBeforeSubmit();
            }
            return false;
        };

        const validate = async () => {
            let valid = true;
            for (const [name, checkFn] of checks) {
                const val = getValueByPath(name);
                if (!await checkFn(val)) {
                    valid = false;
                }
            }
            return valid;
        };

        const clearValidate = () => {
            for (const [name, clearFn] of clears) {
                clearFn();
            }
        };

        expose({
            validate,
            clearValidate,
            resetFields () {
                const initData = deepCopy(initModel);
                Object.assign(props.model, initData);
            },
            validateFields (names: string[]) {
                names.forEach(name => {
                    const check = checks.get(name);
                    check && check(props.model[name]);
                });
            }
        });

        return () => <>
            <form class={{
                'cm-form': true,
                'cm-form-inline': props.inline
            }} onSubmit={onSubmit} autocomplete={props.autocomplete}>
                <button type="submit" style={{display: 'none'}}></button>
                {slots.default?.()}
            </form>
        </>;
    }
});
